package pl.ydp.components.mathcell.mathcell
{
	
	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.text.TextLineMetrics;
	import flash.utils.Timer;
	
	import mx.containers.Box;
	import mx.containers.Panel;
	import mx.containers.VBox;
	import mx.controls.Image;
	import mx.core.UIComponent;
	import mx.events.FlexEvent;
	import mx.events.ResizeEvent;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	
	import pl.ydp.components.mathcell.components.mathtext.MathText;
	import pl.ydp.components.mathcell.mathcell.events.MathCellEvent;
	import pl.ydp.components.mathcell.mathcell.expressions.CellBindingExpressionType;
	import pl.ydp.components.mathcell.mathcell.expressions.CellExpressionManager;
	import pl.ydp.components.mathcell.mathcell.preprocessing.ReScaler;
	import pl.ydp.components.mathcell.mathcell.preprocessing.ScaleAnalyzer;
	import pl.ydp.components.mathcell.mathcell.preprocessing.multicharacter.MultiCharacterContainer;
	import pl.ydp.components.mathcell.mathcell.preprocessing.multicharacter.MultiCharacterContainer5;
	import pl.ydp.components.mathcell.mathcell.preprocessing.structures.ArrayProcessor;
	import pl.ydp.components.mathcell.mathcell.utils.BeforeDrawingEnabledData;
	import pl.ydp.components.mathcell.mathcell.utils.HybridFocusManager;
	import pl.ydp.components.mathcell.mathcell.utils.IMarkAnswerSkinWrapper;
	import pl.ydp.components.mathcell.mathrecognizer.Debug;
	import pl.ydp.components.mathcell.mathrecognizer.characters.CharacterType;
	import pl.ydp.components.mathcell.mathrecognizer.engine.Recognizer;
	import pl.ydp.components.mathcell.mathrecognizer.engine.Report;
	import pl.ydp.components.mathcell.mathrecognizer.engine.database.modelsets.math_basic.ModelSetMathBasic;
	import pl.ydp.components.mathcell.mathrecognizer.geometry.EuclideanVector;
	import pl.ydp.components.mathcell.mathrecognizer.geometry.Primitive;
	import pl.ydp.components.mathcell.mathrecognizer.geometry.classification.PrimitiveType;
	
	[Style(name="lineThickness",type="uint",format="Number",inherit="no")]
	[Style(name="lineColor",type="uint",format="Color",inherit="no")]
	
	[Style(name="canvasBackgroundColor",type="uint",format="Color",inherit="no")]
	[Style(name="canvasBackgroundAlpha",type="Number",format="Number",inherit="no")]
	[Style(name="canvasBorderColor",type="uint",format="Color",inherit="no")]
	[Style(name="canvasBorderStyle",type="String",enumeration="inset,outset,solid,none",inherit="no")]
	[Style(name="canvasBorderThickness",type="uint",format="Number",inherit="no")]
	[Style(name="canvasBorderSides",type="String",enumeration="all,bottom, none",inherit="no")]
	
	[Style(name="approveTime",type="uint",format="Number",inherit="no")]
	
	[Style(name="markerBackgroundColor",type="uint",format="Color",inherit="no")]
	
	[Style(name="fontColorFixed",type="uint",format="Color",inherit="no")]
	[Style(name="fontColorMarker",type="uint",format="Color",inherit="no")]
	[Style(name="fontColorLocked",type="uint",format="Color",inherit="no")]
	[Style(name="fontColorApproved",type="uint",format="Color",inherit="no")]
	
	[Event(name="ContentChanged",type="MathCellEvent")]
	[Event(name="CellFocused",type="MathCellEvent")]
	
	/**
	 * This is main class in mathcell pakage. This class represents 
	 * the cell object that should be appended in the parent components.  
	 */
	public class Cell extends VBox
	{
		/**
		 * Standard constructor.
		 */
		public function Cell()
		{
			super();
						
			drawing = false;
			drawingJustStarted = false;
			markingCorrect = false;
			markingWrong = false;
			canvasBackgroundColorStd = 0xffffff;
			showingAnswer = false;
			locked = false;
			noBorder = false;
			hasFocus = false;
			horizontalScrollPolicy = "off";
			verticalScrollPolicy = "off";
			hybridFocusManager = new HybridFocusManager;
			
			approveTimer = new Timer(1000);
			approveTimer.addEventListener(TimerEvent.TIMER,onApproveTimer);
			
			imageClear= createResetImage();
			
			imageMarkAnswersBox = new Box;
			imageMarkAnswersBox.percentWidth = 100;
			imageMarkAnswersBox.percentHeight = 100;
			
			content = new Panel;
			addChild(content);
			content.layout="absolute";
            content.setStyle("cornerRadius", 0);
			content.setStyle("marginLeft", 0);
			content.setStyle("marginRight", 0);
			content.setStyle("marginTop", 0);
			content.setStyle("borderThicknessLeft", 0);
			content.setStyle("borderThicknessRight", 0);
			content.setStyle("borderThicknessTop", 0);
			content.setStyle("borderThicknessBottom", 0);
			content.setStyle("headerHeight", 0);
			content.setStyle("dropShadowEnabled", "false");
            content.setStyle("verticalScrollPolicy", "off");
            content.setStyle("horizontalScrollPolicy", "off");
        	content.setStyle("backgroundAlpha", 0);
            content.verticalScrollPolicy = "off";
            content.horizontalScrollPolicy = "off";
			content.addEventListener(MouseEvent.MOUSE_DOWN, onContentMouseDown);
			//content.addEventListener(MouseEvent.MOUSE_OVER, onContentMouseDown);
			
			canvas = new CellCanvas();
			canvas.name = "canvas";
			//canvas.width = WIDTH;
			//canvas.height = HEIGHT;
			canvas.percentWidth = 100;
			canvas.percentHeight = 100;
			
			text = new MathText;
			text.name = "text";
			//text.editable = false;
			//text.selectable = false;
			text.focusEnabled = false;
			text.autoSize = true;
			//text.width = WIDTH;
			//text.height = HEIGHT;
			text.percentWidth = 100;
			text.percentHeight = 100;
			text.minWidth = 12;
			text.minHeight = 16;
			text.setStyle("fontSize", (HEIGHT)*FONT_SIZE_COEFF);
			text.setStyle("paddingTop", 0);
            text.setStyle("verticalScrollPolicy", "off");
            text.setStyle("horizontalScrollPolicy", "off");
            text.setStyle("verticalAlign", "middle");
            text.horizontalScrollPolicy = "off";
            text.verticalScrollPolicy = "off";
            defaultFont = ""; 
            //text.setStyle("fontFamily", "Times New Roman");
            //text.wordWrap = false;
            
			
			penPoints = new Array();
			
			//_characterExpected = new Character;
			//_characterSuggested = new Character;
			
			//cellData = new CellData();
			
			//cellTypeCode = CellType.ALL;
			cellStateCode = CellState.OPEN;
			
			expressionManager = new CellExpressionManager();
			cellFieldWidth = 1;
			cellFieldHeight = 1;
			
			recongnizer = new Recognizer(WIDTH, HEIGHT);
			
			charactersContainer = new MultiCharacterContainer(expressionManager.maximumLength, 0, 0, true, 1);
			
			
			
			addEventListener(FlexEvent.CREATION_COMPLETE, this.onCreationComplete);
			addEventListener(MouseEvent.ROLL_OVER, this.onMouseOver);
			addEventListener(MouseEvent.ROLL_OUT, this.onMouseOut);	
			addEventListener(MouseEvent.MOUSE_MOVE, this.onMouseMoveControl);
			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
		}
		
		
		
		public function updateRecognizer( chars:String ):void
		{
			setCharsToRecognize( chars );
			recongnizer = new Recognizer(WIDTH, HEIGHT);
		}
		
		private function setCharsToRecognize( chars:String ):void
		{
			if( chars != null ){
				var characters:Array = chars.split(' ').join('').split(',');
				ModelSetMathBasic.addModelsClasses( characters );
			}
		}
		
		
		
		protected static var classConstructed:Boolean = classConstruct();
		
		/**
		 * Called when static variable is initialized, initiates default styles
		 * for the component.
		 */
        protected static function classConstruct():Boolean {
            if (!StyleManager.getStyleDeclaration("Cell"))
            {
                var style:CSSStyleDeclaration = new CSSStyleDeclaration();
                style.defaultFactory = function():void
                {
                    this.lineColor = 0x000000;
                    this.lineThickness = 2;
                    this.borderColor = 0x000000;
                    this.borderThickness = 0;
                    this.borderStyle = "solid";
                    
                    this.canvasBackgroundColor = 0xffffff;
                    this.canvasBackgroundAlpha = 1;
                    this.canvasBorderColor = 0x000000;
                    this.canvasBorderThickness = 1;
                    this.canvasBorderStyle = "solid";
                    this.canvasBorderSides = "all";
                    
                    this.approveTime = 1500;
                    
                    this.horizontalScrollPolicy = "off";
                    this.verticalScrollPolicy = "off";
                    
                    this.fontColorFixed = 0x000000;
                    this.fontColorLocked = 0x999999;
                    this.fontColorApproved = 0x999999;
                    this.fontColorMarker = 0xff9999;
                    
                    this.markerBackgroundColor = 0x66ffff;
                }
                StyleManager.setStyleDeclaration("Cell", style, true);

            }
            return true;
        }
		
				
		// ------------------------- FIELDS ------------------------- 
		
		/** Minimum cell size (height). */
		public static const SIZE_MIN:Number = 32;
		/** Maximum cell size (height). */
		public static const SIZE_MAX:Number = 128;
		/** The size above which input cell contents won't be scaled to 128px height. */
		public static const SIZE_NOSCALE:Number = 96;
		/** Height to width aspect ratio */
		public static const ASPECT_RATIO:Number = 3/4;
		/** Determines the releation of font size and cell height */
		private static const FONT_SIZE_COEFF:Number = 0.7;
		/** Not used */
		private static const CHARACTERS_COUNT_MAX:Number = 4;
		/** Not used */ 
		private static const MARKER_TEXT_LENGTH_MAX:Number = 2; 
		
		/** Default 1-field cell width */
		protected var WIDTH:Number = 96;
		/** Default 1-field cell height */
		protected var HEIGHT:Number = 128;		
		
		/** Indicates if drawing the shapes by the user is underway */
		private var drawing:Boolean;
		/** Indicates if drawing has jus been started */
		private var drawingJustStarted:Boolean;
		
		//public var cellData:CellData;
		//public var cellTypeCode:Number;
		/** 
		 * Cell state represented as number code
		 * @see CellState.as
		 */
		public var cellStateCode:Number;
		
		//protected var cellExpression:Array;
		//** Determines the count of characters that could be input in the cell */
		//protected var cellExpressionLength:Number;
		/** Determines the width of the cell expressed in characters */
		protected var cellFieldWidth:Number;
		
		/** Determines the height of the cell expressed in characters. 
		 * Amounts to 1 for a cell that won't allow fractions and
		 * 2 for a cell that would allow the input of fractions */
		protected var cellFieldHeight:Number;
		
		/** Manages the expression contents */
		protected var expressionManager:CellExpressionManager;
		
		/** Not used */
		protected var charactersContainer:MultiCharacterContainer;
		
		/** Array of arrays with point coordinates input by user. */ 
		protected var penPoints:Array;	
		
		/** Reset image */
		protected var imageClear:Image;
		/** The Box dedicated to serve MarkAnswerSkin */
		protected var imageMarkAnswersBox:Box;
		
		/** The main panel on which crucial components are placed */
		protected var content:Panel;
		/** The component that receives drawing actions */
		protected var canvas:CellCanvas;
		/** Displaying text in cell */
		protected var text:MathText;
		
		/** Default color of the canvas background, useful when changing the backgound */
		protected var canvasBackgroundColorStd:Number;
		/** Indicates if MarkCorrect action is under way */
		protected var markingCorrect:Boolean;
		/** Indicates if MarkWrong action is under way */
		protected var markingWrong:Boolean;
		/** Indicates if ShowAnswer action is under way */
		protected var showingAnswer:Boolean;
		/** Not used */
		protected var userAnswer:Array;
		/** Indicates if the cell is currently locked */
		protected var locked:Boolean;
		/** 
		 * Control the behaviour of the cell by controlling this value.
		 * Set to <code>true</code> if the cell has been focused.
		 * Set to <code>false</code> if the focus has been removed.
		 * Focus activity should be under control by the parent component. 
		 */
		public var hasFocus:Boolean;
		/** Hybrid focus controller */
		protected var hybridFocusManager:HybridFocusManager;
		/** Stores and manages drawing points that were initialy not drawn because drawing 
		 * was not enabled due to hybrid focus */
		protected var beforeDrawingEnabledData:BeforeDrawingEnabledData;
		
		/** Default font name */
		private var defaultFont:String;
		/** Forces no border cell style */
		public var noBorder:Boolean;
		/** the borer size calcolated to serve drawing properly */
		private var drawingBorderThickness:Number;
		
		/** Characeter recognition controller */
		protected var recongnizer:Recognizer;
		
		/** Timer that controls the input approval (changing drawing shapes into typed characters) */
		protected var approveTimer:Timer;
		
		/** Default reset image source */
		[Embed(source="..\\..\\..\\..\\..\\..\\assets\\x_s.png", mimeType="image/png")]  public var imgClearX:Class;
		
		// ------------------------- PROPERTIES ------------------------- 
				
		/** User width value */
		private var _userWidth:Number = NaN;
		/** User height value */
		private var _userHeight:Number = NaN;
		
		private var isOnStage:Boolean = false;
		
		/**
		 * Sets the desired width of the cell.
		 * If not set, the width is calculated automatically.
		 */
		public function set userWidth(uw:Number):void{
			if (uw >= SIZE_MIN*ASPECT_RATIO  &&  uw <= SIZE_MAX*ASPECT_RATIO){ 
				_userWidth = uw;
			} else if (uw < SIZE_MIN*ASPECT_RATIO){
				_userWidth = SIZE_MIN*ASPECT_RATIO;
			} else if (uw > SIZE_MAX*ASPECT_RATIO){
				_userWidth = SIZE_MAX*ASPECT_RATIO;
			}
		}
		
		/**
		 * @return the width of the cell set by the user.
		 */
		public function get userWidth():Number{
			return _userWidth;
		}
		
		/**
		 * Sets the desired height of the cell.
		 * If not set, the height is calculated automatically.
		 */
		public function set userHeight(uh:Number):void{
			if (uh >= SIZE_MIN  &&  uh <= SIZE_MAX){ 
				_userHeight = uh;
			} else if (uh < SIZE_MIN){
				_userHeight = SIZE_MIN;
			} else if (uh > SIZE_MAX){
				_userHeight = SIZE_MAX;
			}
		}
		
		/**
		 * @return the height of the cell set by the user .
		 */
		public function get userHeight():Number{
			if (!isNaN(_userHeight))
				return _userHeight;
			else if (!isNaN(_userWidth))
				return _userWidth/ASPECT_RATIO;
			else
				return NaN;
		}
		
		override public function get explicitWidth():Number{
			if (!isNaN(userWidth))
				return userWidth*fieldWidth;
			else
				return super.explicitWidth; 
		}
		
		override public function get explicitHeight():Number{
			if (!isNaN(userHeight))
				return userHeight*fieldHeight*userAspectRatio;
			else
				return super.explicitHeight; 
		}
		
		override public function set width(value:Number):void{
			super.width = value;
			
		}	
		
		override public function get maxWidth():Number{
			if (maintainAspectRatio)
				return SIZE_MAX*ASPECT_RATIO*fieldWidth;
			
			return NaN
		}
		
		override public function get maxHeight():Number{
			if (maintainAspectRatio)
				return SIZE_MAX*fieldHeight;
				
			return NaN
		}
		
		
		override protected function measure():void{
			super.measure();
			var paddingV:Number = getStyle("paddingTop") + getStyle("paddingBottom");
			var paddingH:Number = getStyle("paddingLeft") + getStyle("paddingRight");
			
			
			if (maintainAspectRatio){
				measuredMinWidth = Math.round(SIZE_MIN*ASPECT_RATIO*fieldWidth) + paddingH;
				measuredMinHeight = SIZE_MIN*fieldHeight + paddingV;
				measuredWidth = Math.round(SIZE_MAX*ASPECT_RATIO*fieldWidth) + paddingH;
				measuredHeight = SIZE_MAX*fieldHeight + paddingV;
			} else {
				measuredMinWidth = Math.round(SIZE_MIN*ASPECT_RATIO) + paddingH;
				measuredMinHeight = SIZE_MIN + paddingV;
			}
			
		}
		
		override public  function getExplicitOrMeasuredWidth():Number{
			return super.getExplicitOrMeasuredWidth();
		}
		
        override protected function updateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void {
        	        	
            super.updateDisplayList(unscaledWidth, unscaledHeight);
			
			content.width = unscaledWidth;
			content.height = unscaledHeight;//Math.floor(unscaledWidth/ASPECT_RATIO);
            
            updateCellLayout();
            updateMultiCharacterContainer();
        	
        }		

		override public function get percentWidth():Number{
			
			if (isNaN(userWidth))
				return 100*fieldWidth;
			else if (!maintainAspectRatio  &&  !isNaN(userWidth)  &&  !isNaN(userHeight))
				return NaN;
			else if (!maintainAspectRatio)
				return 100;
			else 
				return NaN; 
		} 

		override public function get percentHeight():Number{
			if (isNaN(userWidth))
				return 100;
			else if (!maintainAspectRatio  &&  !isNaN(userWidth)  &&  !isNaN(userHeight))
				return NaN;
			else if (!maintainAspectRatio)
				return 100;
			else 
				return NaN; 
		} 
		
		/** Determines whether the default aspect ratio should be preserved. */ 
		public var maintainAspectRatio:Boolean = true;
		
		/** Calculates the aspect ration of the cell, counter for one place for character */
		public function get actualAspectRatio():Number{
			if (maintainAspectRatio)
				return ASPECT_RATIO;
			else
				return content.width/expressionManager.maximumLength/content.height;
		}
		
		private var _userAspectRatio:Number = 1;
		
		public function set userAspectRatio(value:Number):void{
			_userAspectRatio = value;
		}
		
		public function get userAspectRatio():Number{
			return _userAspectRatio;
		}
		
		/** Font size value determined by the component user */
		private var _userFontSize:Number = NaN;
		
		/** Sets the size of the font that is printed in the cell. */ 
		public function set userFontSize(value:Number):void{
			if (!isNaN(value)  &&  value)
				_userFontSize = value;
		}

		/** @return the size of the font that is printed in the cell. */
		public function get userFontSize():Number{
			return _userFontSize;
		}
		
		/** 
		 * Determines if the default state of the cell is empty or 
		 * filled with the suggested or expected expression. 
		 */
		public function set defaultFilled(df:Boolean):void{
			expressionManager.defaultFilled = df;
		}
		
		/** 
		 * @return the information if cell should be filled with some content in its initial state
		 */
		public function get defaultFilled():Boolean{
			return expressionManager.defaultFilled;
		}
		
		
		public function set alignDecimal(ad:Boolean):void{
			text.alignDecimal = ad;
		}
		
		public function get alignDecimal():Boolean{
			return text.alignDecimal;
		}
		
		//---------------------------------------------------
		
		/**
		 * Sets the source for the reset image button. 
		 */
		public function setImageClear(img:Object):void{			
			if(imageClear && imageClear.parent){				
				imageClear.parent.removeChild(imageClear);
				imageClear = createResetImage();				
				loadImageClear(img);
				content.addChild(imageClear);			
				imageClear.addEventListener(MouseEvent.MOUSE_DOWN, this.startDrawing);
				imageClear.addEventListener(MouseEvent.MOUSE_UP, this.stopDrawing);
				imageClear.addEventListener(IOErrorEvent.IO_ERROR,onImageResetIOError);
				
			}
			else{				
				loadImageClear(img);
			}			
		}		
		
		
		private function loadImageClear(img:Object):void{
			if(img is String)
				imageClear.load(img)
			else		
				imageClear.source = img;
		}
		
		private function onImageResetIOError(event:IOErrorEvent):void{
			trace("error laod image ", event.text);
		}
		
		/** Determines whether the cell can receive focus. */
		public function get focusable():Boolean{
			return cellStateCode != CellState.FIXED;
		} 
		
		/**
		 * Determines if cell is of marker type.
		 * Not used.
		 */
		private function isMarker():Boolean{
			return false;
		} 

		/** 
		 * Updates the state of the cell basing on the 
		 * actual cell expression.
		 */
		public function updateCellState():void{
			var newCellState:Number;
			
			if (expressionManager.bindingExpressionType == CellBindingExpressionType.FIXED){
				newCellState = CellState.FIXED;
			} else if (expressionManager.getExpressionDisplay(showingAnswer).length > 0){
				newCellState = CellState.LOCKED;
			} else {
				newCellState = CellState.OPEN;
			}
			
			if (cellStateCode != newCellState){
				cellStateCode = newCellState;
				clearDrawing();
				cellStateChanged();
			} else if (cellStateCode == CellState.LOCKED) {
				updateDisplayText();
			}
		}
		
		/** 
		 * @return Cell state number code
		 * @see CellState.as 
		 */
		public function get cellState():Number{
			return cellStateCode;
		}
		
		
		//---------------------------------------------------

		
		/**
		 * Sets the fixed expression for the cell.
		 */
		public function set expressionFixed(pexpression:String):void{
			
			expressionManager.expressionFixed = pexpression;
			/*
			 if (expressionManager.bindingExpressionType == CellBindingExpressionType.FIXED){
				cellType = "all";
				cellState = "fixed";
			 }
			 */
			 updateCellState();
			 updateFieldSize();
		}	
			
		/** @return The fixed expression, if set. */
		public function get expressionFixed():String{
			return expressionManager.expressionFixed;
		}
		
		// expressionSuggested
		/**
		 * Sets the suggested answer for the cell. If the user
		 * provides the naswer different than the expression suggested
		 * it is NOT a mistake. Suggested expression is only for
		 * displaying a hint ("show answers" functionality)
		 */ 				
		public function set expressionSuggested(pexpression:String):void{
			
			expressionManager.expressionSuggested = pexpression;

			updateCellState();
			updateFieldSize();
		}	
			
		/** @return The suggested expression. */
		public function get expressionSuggested():String{
			
			return expressionManager.expressionSuggested;
		}
				
		// expressionExpected
		/**
		 * Sets the expected answer for the cell. The user answer
		 * should be the same as the expected expression. Otherwise
		 * it will be a mistake.
		 */
		public function set expressionExpected(pexpression:String):void{
				
			expressionManager.expressionExpected = pexpression

			updateCellState();
			updateFieldSize();
		}	
			
		/** @return The expected answer. */
		public function get expressionExpected():String{
			return expressionManager.expressionExpected;
		}
					
		/** Updates the size of the cell field */
		private function updateFieldSize():void{
			if (!isNaN(userCellFieldWidth))
				cellFieldWidth = userCellFieldWidth;
			else
				cellFieldWidth = expressionManager.maximumLength;
				
			if (!isNaN(userCellFieldHeight))
				cellFieldHeight = userCellFieldHeight;
			else
				cellFieldHeight = 1;
				
			expressionManager.allowFractions = (cellFieldHeight == 2  ||  !maintainAspectRatio);
			updateMultiCharacterContainer();
		}
		
		/** 
		 * Updated the multiple character container due to cell size changes.
		 * Depreciated as MultiCharacterContainer5 is developed.
		 */
		private function updateMultiCharacterContainer():void{
			
			charactersContainer.cellWidth = content.width/fieldWidth;
			charactersContainer.cellHeight = content.height/fieldHeight;
			charactersContainer.recursiveLevels = (!expressionManager.allowFractions) ? 1 : 3;
		}
		
		/** @return The current expression provided by the user. */
		public function get expressionContent():String{
			return expressionManager.expressionContent; 
		}
		
		// expressionLength
		/** @return The maximum possible length of the user answer. */
		public function get maximumExpressionLength():Number{
			return expressionManager.maximumLength;
		}		
		
		//private var userExpressionLength:Number;
		/**
		 * Sets the maximum possible length of the user answer.
		 */
		public function set maximumExpressionLength(mel:Number):void{
			expressionManager.maximumLength = mel;
			updateFieldSize();
		}		
		
		/** Field width value */
		private var userCellFieldWidth:Number = NaN;
		
		/**
		 * Sets the width of the field. Field width determines the number
		 * of character that can horizontally fit the cell. In other words, 
		 * this is the width of the cell expressed in characters. With field 
		 * width of 2 the cell would be twice wider than 1. 
		 * 
		 * @see fieldHeight
		 */
		public function set fieldWidth(value:Number):void{
			userCellFieldWidth = value;
			updateFieldSize();
		}
		
		/** @return Field width. */
		public function get fieldWidth():Number{
			return cellFieldWidth;
		}		
		
		/** Field height value */
		private var userCellFieldHeight:Number = NaN;
		
		/**
		 * Sets the height of the field. Field height determines the number
		 * of character that can vertically fit the cell. If the cell should
		 * accept the fractions, the field height should amount to 2 or more.
		 */
		public function set fieldHeight(value:Number):void{
			userCellFieldHeight = value;
			updateFieldSize();
		}
		
		/** @return Field height. */
		public function get fieldHeight():Number{
			return cellFieldHeight;
		}		
		
		public function set onlyFraction(of:Boolean):void{
			expressionManager.onlyFraction = of;
		}		
		
		public function get onlyFraction():Boolean{
			return expressionManager.onlyFraction;
		}
		
		public function set allowFractionReduction(afr:Boolean):void{
			expressionManager.allowFractionReduction = afr;
		}		
		
		public function get allowFractionReduction():Boolean{
			return expressionManager.allowFractionReduction;
		}
		
		
		//---------------------------------------------------
		
		/** 
		 * Inputs the character for the cell. Utilized for serving keyboard
		 * input.
		 */
		public function inputLockedCharacter(charCode:Number):Boolean{
			
			if (cellStateCode == CellState.FIXED)
				return false;
				
			if (locked || showingAnswer  ||  markingCorrect  ||  markingWrong)
				return false;
			
			var inputResult:Boolean = false;
			var append:Boolean = false;
			
			var nchar:String = String.fromCharCode(charCode);
			
			inputResult = expressionManager.inputExpressionUserCharacter(nchar);
				
			if (inputResult){
				clearDrawing();
				approveTimer.stop();
			 	updateCellState();
			 	updateDisplayText();
				dispatchEvent(new MathCellEvent(MathCellEvent.CONTENT_CHANGED, this));				
				return true;
			}
			
			return false;
		}
		
		public function updateCharsReplacements( replacements:String ):void
		{
			expressionManager.updateCharsReplacements( replacements );
		}
		
		/**
		 * Erases the input locked character, all of the or only 
		 * the last one (ex. handle BackSpace), depending on the 
		 * param value.
		 */
		public function clearLockedCharacter(onlyLastCharacter:Boolean = false):void{
			if (locked || showingAnswer  ||  markingCorrect  ||  markingWrong)
				return;
				
			if (onlyLastCharacter)
				expressionManager.removeLastCharacterExpressionUser();
			else
				expressionManager.clearExpressionUser();
				
			if (cellStateCode == CellState.LOCKED){
				
				updateCellState();
				updateDisplayText();
				dispatchEvent(new MathCellEvent(MathCellEvent.CONTENT_CHANGED, this));
			}
		}
		
		/** 
		 * Inputs one or more characters (expression) to the cell.
		 * 
		 * @param expr The expression string to input.
		 * @param userChanged Determines, if the change has been triggered by the user action.
		 */
		public function inputLockedExpression(expr:String, userChanged:Boolean = true):void{
			if (locked)
				return;
			
			inputExpression(expr, userChanged);
		}
		
		public function inputExpression(expr:String, userChanged:Boolean = true):void
		{
			if ( showingAnswer  ||  markingCorrect  ||  markingWrong)
				return;
			
			if (cellStateCode == CellState.FIXED)
				return;
			
			var inputResult:Boolean = expressionManager.setExpressionUser(expr);
			/*
			cellExpression = new Array();
			
			for (var i:Number = 0 ; i < expr.length ; i ++){
			cellExpression.push(new Character(expr.charAt(i)));
			}
			*/
			
			updateCellState();
			updateDisplayText();
			
			dispatchEvent(new MathCellEvent(MathCellEvent.CONTENT_CHANGED, this, false, false, userChanged));
		}
		
		
		// ------------------------- PROPERTIES METHODS -------------------------
		
		/**
		 * Called when the call has changed its state (FIXED/LOCKED/OPEN).
		 */
		protected function cellStateChanged():void{
			
			if (content.getChildren().indexOf(canvas) != -1){
				content.removeChildAt(content.getChildIndex(canvas));	
				canvas.removeEventListener(MouseEvent.MOUSE_DOWN, this.startDrawing);
				stage.removeEventListener(MouseEvent.MOUSE_UP, this.stopDrawing);	
				stage.removeEventListener(MouseEvent.ROLL_OUT, this.stopDrawing);		
			} 
			if (content.getChildren().indexOf(text) != -1){
				content.removeChildAt(content.getChildIndex(text));	
			}
			if (content.getChildren().indexOf(imageClear) != -1){
				content.removeChildAt(content.getChildIndex(imageClear));
				imageClear.removeEventListener(MouseEvent.MOUSE_DOWN, this.startDrawing);
				imageClear.removeEventListener(MouseEvent.MOUSE_UP, this.stopDrawing);
			}
			
			
			if (cellStateCode == CellState.FIXED){
				content.addChild(text);
				text.text = expressionManager.getExpressionDisplay(showingAnswer);
			} else if (cellStateCode == CellState.LOCKED){//  &&  cellExpression.length > 0){//&&  cellData.character.code > 0){
				content.addChild(text);
				content.addChild(imageClear);
				text.text = expressionManager.getExpressionDisplay(showingAnswer);
			} else {
				content.addChild(canvas);
				content.addChild(imageClear);
				canvas.addEventListener(MouseEvent.MOUSE_DOWN, this.startDrawing);
				stage.addEventListener(MouseEvent.MOUSE_UP, this.stopDrawing);	
				stage.addEventListener(MouseEvent.ROLL_OUT, this.stopDrawing);
				imageClear.addEventListener(MouseEvent.MOUSE_DOWN, this.startDrawing);
				imageClear.addEventListener(MouseEvent.MOUSE_UP, this.stopDrawing);
			}
			
			if (content.getChildren().indexOf(imageMarkAnswersBox) != -1){
				content.setChildIndex(imageMarkAnswersBox, getChildren().length);
			}
			
			//updateCellLayout();
			styleChanged("cellStateStyles");
			//process();
		}
		
		/**
		 * Updates the text tha should currently be displayed in the cell.
		 */
		private function updateDisplayText():void{
			if (cellStateCode != CellState.OPEN){
				
				var textToDisplay:String = expressionManager.getExpressionDisplay(showingAnswer);
				text.text = textToDisplay;
				updateCellLayout();
			}
		}

		// ------------------------- STYLE ------------------------- 
		
        override public function styleChanged(styleProp:String):void {

			if (styleProp == "cellStateStyles"){
				
				super.styleChanged(null);
				
				updateContentStyles();
				
			} else{ 
				
				super.styleChanged(styleProp);
				
				if (styleProp=="lineThickness" || styleProp=="lineColor"  ||  
	            	styleProp == "borderThickness" ||   styleProp == "canvasBackgroundColor" ||
	            	styleProp == "canvasBorderColor" ||   styleProp == "canvasBorderStyle" ||
	            	styleProp == "canvasBorderThickness" ||  styleProp == "canvasBorderSides"  ||
	            	styleProp=="approveTime" || 
	            	styleProp == null) 
	            	{
	            	
            		updateContentStyles();      
            		 	
            		approveTimer.delay = getStyle("approveTime");
	            		            	            	
	            	invalidateDisplayList();
					
           		 	}
  			 }
  			 
  			 updateCellLayout();
            
             if (styleProp=="borderThickness"  || styleProp=="borderColor" ||   styleProp == null){
             	createBorder();
             }
             
        }
        
        /**
         * Updates the styles that describe the content
         */
        private function updateContentStyles():void{
        	
        	canvas.setStyle("backgroundColor", getStyle("canvasBackgroundColor"));
        	canvas.setStyle("backgroundAlpha", getStyle("canvasBackgroundAlpha"));
			canvas.setLineStyle(getStyle("lineThickness"),getStyle("lineColor"));
						
			if (getStyle("canvasBorderSides") == "all"  ||   getStyle("canvasBorderSides") == null){
        		canvas.setBorderStyle(getStyle("canvasBorderColor"), getStyle("canvasBorderStyle"), getStyle("canvasBorderThickness"), "right left top bottom");
        		text.setStyle("borderSides", "right left top bottom");
			} else if  (getStyle("canvasBorderSides") == "bottom"){
				canvas.setBorderStyle(getStyle("canvasBorderColor"), getStyle("canvasBorderStyle"), getStyle("canvasBorderThickness"), "bottom" );
				text.setStyle("borderSides", "bottom");
			} else {
				canvas.setBorderStyle(getStyle("canvasBorderColor"), getStyle("canvasBorderStyle"), getStyle("canvasBorderThickness"), "" );
				text.setStyle("borderSides", "");
			}
        	
        	if (isMarker())
        		text.setStyle("backgroundColor", getStyle("markerBackgroundColor"));
        	else
        		text.setStyle("backgroundColor", getStyle("canvasBackgroundColor"));
        	text.setStyle("backgroundAlpha", getStyle("canvasBackgroundAlpha"));
        		
        		
        	if (cellStateCode == CellState.FIXED){
        		
        		if (isMarker())
        			text.setStyle("color", getStyle("fontColorMarker"));
        		else 
        			text.setStyle("color", getStyle("fontColorFixed"));
        			
        	} else if (cellStateCode == CellState.LOCKED) {
				text.setStyle("color", getStyle("fontColorLocked"));
        	}
        	        	
        	text.setStyle("borderColor", getStyle("canvasBorderColor"));
        	text.setStyle("borderStyle", getStyle("canvasBorderStyle"));
        	text.setStyle("borderThickness", getStyle("canvasBorderThickness"));	
        }
        
		/**
		 * Updates the layout, borders, margins, paddings of the cell.
		 */
        private function updateCellLayout():void{
            
        	var borderSize:Number = getStyle("canvasBorderThickness")*((getStyle("canvasBorderStyle") != "none"  &&  getStyle("canvasBorderSides") == "all")? 1 : 0);
        	borderSize = (!isNaN(borderSize)?borderSize:0);
        	
        	var borderSizeBottom:Number = getStyle("canvasBorderThickness")*((getStyle("canvasBorderStyle") != "none"  &&  getStyle("canvasBorderSides") != "none")? 1 : 0);
        	borderSizeBottom = (!isNaN(borderSizeBottom)?borderSizeBottom:0);
        	
			updateImageClearPosition(null);
			
        	imageMarkAnswersBox.setStyle("paddingTop", borderSize);
        	imageMarkAnswersBox.setStyle("paddingLeft", borderSize);
        	imageMarkAnswersBox.setStyle("paddingRight", borderSize);
        	imageMarkAnswersBox.setStyle("paddingBottom", borderSizeBottom);
        	
        	
        	var fontsize:Number;
        	if (isNaN(userFontSize)){
				if (content.height > 0){
					var currentAspectRatioCoeff:Number = (content.width/fieldWidth) / (content.height/fieldHeight);
		        	fontsize = (content.height/fieldHeight-borderSize*2)*0.65;
		            text.setStyle("fontSize", fontsize);
				}
         	} else {
         		fontsize = userFontSize;
         		text.setStyle("fontSize", userFontSize);
         	}
         	
			var stdPaddingTop:Number = (content.height/fieldHeight-fontsize/FONT_SIZE_COEFF)/2;
			
			if (stdPaddingTop < 0)
				stdPaddingTop = 0;
			
			/* padding top removed task 36624 */
			if (!isNaN(stdPaddingTop)  &&  content.height > 0){
	            if (expressionManager.getExpressionDisplay(showingAnswer) == "*"){
	            	text.setStyle("paddingTop", fontsize/4);//stdPaddingTop+fontsize/5);
	            } else {
	            	text.setStyle("paddingTop",0);//stdPaddingTop);
	            }
   			}
           
           text.setStyle("letterSpacing", 0);
            	
        	if (defaultFont == ""  ||  defaultFont == null)
        		defaultFont = getStyle("fontFamily");
        	if (defaultFont == ""  ||  defaultFont == null)
        		defaultFont = text.getStyle("fontFamily");
        		
            if (expressionManager.getExpressionDisplay(showingAnswer) == "·"  ||  expressionManager.getExpressionDisplay(showingAnswer) == ":"  ||  
            	expressionManager.getExpressionDisplay(showingAnswer) == "."  ||  expressionManager.getExpressionDisplay(showingAnswer) == "÷"){
            	if (defaultFont != ""  &&  defaultFont != null)
            		text.setStyle("fontFamily", "Times New Roman");
				
            } else if (defaultFont != ""  &&  defaultFont != null){
            	text.setStyle("fontFamily", defaultFont);
				
            }
            
            if (expressionManager.getExpressionDisplay(showingAnswer) == "≤"  ||  expressionManager.getExpressionDisplay(showingAnswer) == "≥"){
        		text.setStyle("fontWeight", "bold");
        	} else{
        		text.setStyle("fontWeight", "normal");
        	} 
        	
            	
        
			//text.setStyle("paddingTop", Math.floor((content.height-content.width)/2));   	
        }
		
		private function updateImageClearPosition(e:Event):void{
			
			if (content.width > 0){
				var borderSize:Number = getStyle("canvasBorderThickness")*((getStyle("canvasBorderStyle") != "none"  &&  getStyle("canvasBorderSides") == "all")? 1 : 0);
				borderSize = (!isNaN(borderSize)?borderSize:0);
				
				var imageClearWidth:Number = (imageClear.width != 0) ? imageClear.width : 19;
				imageClear.x = content.width - imageClearWidth - borderSize;
				imageClear.y = borderSize;
			}
		}
		
		/**
		 * Measures the given text. Measures width and height only. Expressions with
		 * fractions are supported.
		 * 
		 * Before calling this method please set userFontSize property.
		 */
		override public function measureText(t:String):TextLineMetrics{
			
			var mt:MathText = new MathText;
			if (defaultFont != null  &&  defaultFont != ""){
				mt.setStyle("fontFamily", defaultFont);	
			} else if (getStyle("fontFamily") != null &&  getStyle("fontFamily") != "") {
				mt.setStyle("fontFamily", getStyle("fontFamily"));
			}
			if (!isNaN(userFontSize))
				mt.setStyle("fontSize", userFontSize);
			mt.regenerateStyleCache(false);
			return mt.measureText(t);
		}
		
		// ------------------------- EVENT HANDLERS ------------------------- 
		
		/**
		 * Called on component's CreationComplete event.
		 */
		private function onCreationComplete(e:FlexEvent):void{
			
		}
		
		protected function onAddedToStage(event:Event):void{
			isOnStage = true;
			cellStateChanged();
		}
		
		private function onRemovedFromStage(e:Event):void{
			
			callLater(function ():void{
				if (!isOnStage){
					if(approveTimer){
						approveTimer.reset();
						approveTimer.removeEventListener(TimerEvent.TIMER,onApproveTimer);
					}
					
					if (stage != null){
						stage.removeEventListener(MouseEvent.MOUSE_MOVE, processDrawing);
						stage.removeEventListener(MouseEvent.MOUSE_UP, stopDrawing);	
						stage.removeEventListener(MouseEvent.ROLL_OUT, stopDrawing);
					}
					
					removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
					removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
				}
			});
			isOnStage = false;
		}
		
		/**
		 * Called when the timer that controls input approval fires. 
		 */
		protected function onApproveTimer(e:TimerEvent):void{
			approveTimer.stop();
			process();
			if (expressionManager.expressionUser.length > 0){
				cellStateCode = CellState.LOCKED;
				cellStateChanged();
			} else {
				clear();
			}
		}
		
		// -----------
		
		/**
		 * Starts drawing in the data layer (there is data layer and view layer controlled
		 * by the CellCanvas class).
		 */
		private function startDrawing(me:MouseEvent):void{
			if (locked){
				return;
			}
			drawingBorderThickness = getStyle("canvasBorderThickness")*((getStyle("canvasBorderStyle") != "none"  &&  getStyle("canvasBorderSides") == "all")? 1 : 0);
            drawingBorderThickness = (!isNaN(drawingBorderThickness)?drawingBorderThickness:0);
            
			approveTimer.stop();
			stage.addEventListener(MouseEvent.MOUSE_MOVE, processDrawing);
			drawing = true;
			//imageClear.visible = false;
			drawingJustStarted = true;
			penPoints.push(new Array());
			unmark();
			
			hybridFocusManager.startDrawing(hasFocus);
			beforeDrawingEnabledData = new BeforeDrawingEnabledData();
			beforeDrawingEnabledData.pushPoint(new Point(canvas.mouseX, canvas.mouseY));
			penPoints[penPoints.length-1].push(new Point(canvas.mouseX, canvas.mouseY));
		}
		
		/**
		 * Finished the drawing.
		 */
		private function stopDrawing(me:MouseEvent):void{
			if (drawing  &&  !(me.relatedObject == imageClear  ||  (me.relatedObject != null  &&  me.relatedObject.parent == imageClear))){
				drawing = false;

//				Application.application.removeEventListener(MouseEvent.MOUSE_MOVE, processDrawing);
				
				penPoints[penPoints.length-1].push(new Point(canvas.mouseX, canvas.mouseY));
				
				if (drawingJustStarted  &&  hybridFocusManager.drawingEnabled()){
					canvas.startDrawing(canvas.mouseX, canvas.mouseY);
				}
				drawingJustStarted = false;
				if (hybridFocusManager.drawingEnabled()){
					canvas.stopDrawing(canvas.mouseX+1, canvas.mouseY+1);
					drawingFinished();
				}
				//imageClear.visible = true;
				
				if (!hybridFocusManager.drawingEnabled()){
					penPoints.pop();
				}		
			}
		}
		
		/**
		 * Processes the drawing action. The cricual dytu of this function is 
		 * to update Pen Points.
		 * 
		 * @see penPoints.
		 */
		private function processDrawing(me:MouseEvent):void{
			hybridFocusManager.processDrawing();
			if (drawing){
				
				penPoints[penPoints.length-1].push(new Point(canvas.mouseX, canvas.mouseY));
								
				if (hybridFocusManager.drawingEnabled()){
					if (drawingJustStarted){
						var tmpPoint:Point;
						var usedPointsFromBDED:Boolean = false;
						if (beforeDrawingEnabledData.hasMorePoints()){
							tmpPoint = beforeDrawingEnabledData.shiftPoint();
							usedPointsFromBDED = true;
						} else {
							tmpPoint = new Point(canvas.mouseX, canvas.mouseY);
						}
						
						canvas.startDrawing(tmpPoint.x, tmpPoint.y);
						
						while (beforeDrawingEnabledData.hasMorePoints()){
							tmpPoint = beforeDrawingEnabledData.shiftPoint();
							canvas.processDrawing(tmpPoint.x, tmpPoint.y);
						}
						
						if (usedPointsFromBDED)
							canvas.processDrawing(canvas.mouseX, canvas.mouseY);
						
						drawingJustStarted = false;
					} 
					canvas.processDrawing(canvas.mouseX, canvas.mouseY);
				} else {
					beforeDrawingEnabledData.pushPoint(new Point(canvas.mouseX, canvas.mouseY));
				}
			}
			
		}
			
		// -----------

		/**
		 * Controls the display (alpha) of the reset button.
		 */
		protected function onMouseMoveControl(me:MouseEvent):void{
			var coeff:Number = 1;
			if (drawing){
				//return;
				coeff = 0.5;
			}
			var a:Number = 0.75 - (content.height - 16 - content.mouseY)/(content.height-16)*coeff;
			a = 1-a;
			if (a < .1){
				a = .1;
			} else if (a > 1){
				a = 1;
			} 
			imageClear.alpha = a;
		}
		
		/**
		 * Controls the display of the reset button.
		 */
		protected function onMouseOver(me:MouseEvent):void{
			if (locked || showingAnswer  ||  markingCorrect  ||  markingWrong)
				return;
				
			imageClear.visible = true;
		}
		
		/**
		 * Controls the display of the reset button.
		 */
		protected function onMouseOut(me:MouseEvent):void{
			imageClear.visible = false;
			
		}			
		
		/**
		 * Called when user click clear (reset) button.
		 */
		private function onClearClick(me:MouseEvent):void{
			if (drawing)
				stopDrawing(me);
			clear();
			
		}	
		
		/**
		 * Called when user presses the mouse in the area of content.
		 */
		private function onContentMouseDown(me:MouseEvent):void{
			if (!focusable)
				return;
				
			dispatchEvent(new MathCellEvent(MathCellEvent.CELL_FOCUSED,this));
		}	
		
		
		// ------------------------- FOCUS -------------------------
		
		/**
		 * Sets the border style for the cell.
		 *
		 * Handling the focus by the parent component may require the
		 * change of the cell style. This function should change the 
		 * border style every time the cell changes its focus mode.
		 */
		public function setBorderStyle(color:Number, style:String, thickness:Number, sides:String, force:Boolean = false):void{
			if (noBorder &&  !force){
				setStyle("canvasBorderThickness", 0);
				return;
			}
			setStyle("canvasBorderColor", color);
			setStyle("canvasBorderStyle", style);
			setStyle("canvasBorderThickness", thickness);
			setStyle("canvasBorderSides", sides);
			
			var drawingBorderThickness2:Number = getStyle("canvasBorderThickness")*((getStyle("canvasBorderStyle") != "none"  &&  getStyle("canvasBorderSides") == "all")? 1 : 0);
            drawingBorderThickness = (!isNaN(drawingBorderThickness2)?drawingBorderThickness2:drawingBorderThickness);
		}
		
				
		// ------------------------- PRIVATE METHODS -------------------------
		
		private function drawingFinished():void{
			approveTimer.start();
		}
		
		/**
		 * This is the main function in this components. This function
		 * triggers the processing of the curves input by the user.
		 */
		public function process():void{
			
			if (penPoints.length > 0  &&  cellStateCode == CellState.OPEN){
				Debug.clearLog();
				
				expressionManager.clearExpressionUser();
				
				//charactersContainer.penPoints = ArrayProcessor.cloneRecursive(penPoints);
				
				var charactersContainer5:MultiCharacterContainer5 = new MultiCharacterContainer5(ArrayProcessor.cloneRecursive(penPoints),
					expressionManager.maximumLength, content.width/fieldWidth, content.height/fieldHeight, fieldHeight >= 2);
				
				var scaleDatas:Array = ScaleAnalyzer.getCharactersHeights(charactersContainer5);
				
				var SCALE_MARGIN:Number = 8;
				
				var maxScale:Number = SIZE_MAX * 2 / (content.height/fieldHeight);
				var scaleCharacter:Number;
				var scaleField:Number = (SIZE_MAX-SCALE_MARGIN) / (content.height/fieldHeight);
				
				var char:Number;

				var scaledPenPoints:Array;
				var d:Number;
				var report:Report;
				
				//num0 = null;
				//num1 = null;
				//den0 = null;
				//den1 = null;
				
				//primitives = new Array;
				//scaledPP = new Array;
				
				for (char = 0 ; char <  charactersContainer5.charactersCount ; char ++){
					if (charactersContainer5.isFractionAt(char)){
						
						var numChars:Array = new Array;
						var denChars:Array = new Array;
						
						// NUMERATOR
						
						for (d = 0 ; d < charactersContainer5.getFractionNumeratorCharactersPointsAt(char).length ; d ++){

							scaleCharacter = (SIZE_MAX-SCALE_MARGIN) / scaleDatas[char][0][d].vertical.size;
							if (scaleCharacter > (SIZE_MAX-SCALE_MARGIN)*ASPECT_RATIO / scaleDatas[char][0][d].horizontal.size)
								scaleCharacter = (SIZE_MAX-SCALE_MARGIN)*ASPECT_RATIO / scaleDatas[char][0][d].horizontal.size;
							if (scaleCharacter > maxScale)
								scaleCharacter = maxScale;
							if (scaleCharacter < 1)
								scaleCharacter = 1;
							scaledPenPoints = ReScaler.scalePointsUp(charactersContainer5.getFractionNumeratorCharactersPointsAt(char)[d], 
								scaleCharacter, scaleDatas[char][0][d], scaleField, SCALE_MARGIN/2);
							
							report = recongnizer.recognizeAndReport(scaledPenPoints, CharacterType.DIGIT);
							
							if (report.code != -1)
								numChars.push(String.fromCharCode(report.code));
							
							/*if (d == 0){
								num0 = report.results;
							} else if (d == 1){
								num1 = report.results;
							}*/
						}
						
						
						// DENOMINATOR
						
						for (d = 0 ; d < charactersContainer5.getFractionDenominatorCharactersPointsAt(char).length ; d ++){
						
							scaleCharacter = (SIZE_MAX-SCALE_MARGIN) / scaleDatas[char][1][d].vertical.size;
							if (scaleCharacter > (SIZE_MAX-SCALE_MARGIN)*ASPECT_RATIO / scaleDatas[char][1][d].horizontal.size)
								scaleCharacter = (SIZE_MAX-SCALE_MARGIN)*ASPECT_RATIO / scaleDatas[char][1][d].horizontal.size;
							if (scaleCharacter > maxScale)
								scaleCharacter = maxScale;
							if (scaleCharacter < 1)
								scaleCharacter = 1;
							scaledPenPoints = ReScaler.scalePointsUp(charactersContainer5.getFractionDenominatorCharactersPointsAt(char)[d], 
								scaleCharacter, scaleDatas[char][1][d], scaleField, SCALE_MARGIN/2);
							
							//scaledPP.push( scaledPenPoints);
							
							report = recongnizer.recognizeAndReport(scaledPenPoints, CharacterType.DIGIT);
							
							//for each (var primobj:Object in report.primitives)
							//	primitives.push(primobj);
										
							if (report.code != -1)
								denChars.push(String.fromCharCode(report.code));
							
							/*if (d == 0){
								den0 = report.results;
							} else if (d == 1){
								den1 = report.results;
							}*/
						}
						
						if (numChars.length > 0  &&  denChars.length > 0){
							expressionManager.appendExpressionUser("|");
							
							for each (var currNumChar:String in numChars)
								expressionManager.appendExpressionUser(currNumChar);

							expressionManager.appendExpressionUser("/");
							
							for each (var currDenChar:String in denChars)
							expressionManager.appendExpressionUser(currDenChar);
							
							expressionManager.appendExpressionUser("|");
						}
						
					} else {
						
						scaleCharacter = (SIZE_MAX-SCALE_MARGIN) / scaleDatas[char].vertical.size;
						if (scaleCharacter > (SIZE_MAX-SCALE_MARGIN)*ASPECT_RATIO / scaleDatas[char].horizontal.size)
							scaleCharacter = (SIZE_MAX-SCALE_MARGIN)*ASPECT_RATIO / scaleDatas[char].horizontal.size;
						if (scaleCharacter > maxScale)
							scaleCharacter = maxScale;
						if (scaleCharacter < 1)
							scaleCharacter = 1;
						if (charactersContainer5.isDotsAt(char))
							scaleCharacter = 1;
						scaledPenPoints = ReScaler.scalePointsUp(charactersContainer5.getCharacterPointsAt(char), 
							scaleCharacter, scaleDatas[char], scaleField, SCALE_MARGIN/2);
						
						//scaledPP.push( scaledPenPoints);
						
						report = recongnizer.recognizeAndReport(scaledPenPoints, CharacterType.ANY);
						
						//for each (var primobj:Object in report.primitives)
						//	primitives.push(primobj);
						
						expressionManager.appendExpressionUser(String.fromCharCode(report.code));
					}
				}
												
				
			} else if (cellStateCode == CellState.OPEN) {
				expressionManager.clearExpressionUser();
			}
			
			dispatchEvent(new MathCellEvent(MathCellEvent.CONTENT_CHANGED, this));
			
		}
		//public var primitives:Array;
		//public var scaledPP:Array;
		//public var num0:Array;
		//public var num1:Array;
		//public var den0:Array;
		//public var den1:Array;
		//-------------------------------------------------------------------
		
		/**
		 * Clears the sprite on which shapes are being drawn.
		 */
		protected function clearSprite(drawingSprite:Sprite):void{
			
			drawingSprite.graphics.clear()
			drawingSprite.graphics.lineStyle(getStyle("lineThickness"),getStyle("lineColor"));
		}
		//-------------------------------------------------------------------
		
		/**
		 * Used to draw primitives os the shapes that were processed.
		 */
		protected function drawPrimitives(primitives:Array, drawingSprite:Sprite):void{

			//var drawingSprite:Sprite = new Sprite;
			
			
			for each (var primitive:Primitive in primitives){
				
				if (PrimitiveType.isLine(primitive.type)   &&  !PrimitiveType.isWave(primitive.type)){
					
					drawingSprite.graphics.moveTo(primitive.begin.x, primitive.begin.y);
					drawingSprite.graphics.lineTo(primitive.end.x, primitive.end.y);
					
				} else if (primitive.type != PrimitiveType.NONE){
					
					drawingSprite.graphics.moveTo(primitive.vectors[0].point.x-1, primitive.vectors[0].point.y-1);
					
					for each (var v:EuclideanVector in primitive.vectors){
						drawingSprite.graphics.lineTo(v.point.x, v.point.y);	
					}
					
				}
			}
			
		}
		
		//-------------------------------------------------------------------
		
		/**
		 * Not used.
		 */
		protected function drawPoints(drawingSprite:Sprite):void{
			if (penPoints.length == 0){
				return;
			}
			try{
				drawingSprite.graphics.clear();
				drawingSprite.graphics.lineStyle(getStyle("lineThickness"),getStyle("lineColor"));
				
				for each (var currArr:Array in penPoints){
					
					drawingSprite.graphics.lineTo(currArr[0].x, currArr[0].y);
					drawingSprite.graphics.moveTo(currArr[0].x+1, currArr[0].y);
					
					for (var k:Number = 1 ; k < currArr.length ; k ++ ) {
						drawingSprite.graphics.moveTo(currArr[k].x, currArr[k].y);	
					}
					
				}
				
			} catch (err:Error){
				//trace( err.message +"\n" + err.getStackTrace() );
			}
			
		}
		
		// ------------------------- PUBLIC METHODS -------------------------
		
		/** @return Array of arrays of point input by the user. */
		public function get points():Array{
			return penPoints;
		}
		
		/** Sets the points array. */ 
		public function set points(p:Array):void{
			penPoints = p;
			process();
		}
		
		//-------------------------------------------------------------------
		
		/** Clears the current contents of the cell. */
		public function clear():void{
			
			if (locked || showingAnswer  ||  markingCorrect  ||  markingWrong)
				return;
			
			if (cellStateCode != CellState.FIXED){
				clearDrawing();
				unmark();
				clearLockedCharacter(false);
				
			}
		} 
		
		//-------------------------------------------------------------------
		
		/**
		 * Clears the area of drawing.
		 */
		private function clearDrawing():void{
			penPoints = new Array();
			drawing = false;
			canvas.clear();
			if (approveTimer.running){
				approveTimer.stop();
			}
			
		}
		
		
		//-------------------------------------------------------------------
		
		/** Triggers the drawing and processing of the characters. */ 
		public function redraw():void{
			process();
		} 
		
		//-------------------------------------------------------------------
		
		/** Resets the cell contents to the default state. */
		public function reset():void{
			
			//clear();
			clearDrawing();
			unmark();
			expressionManager.reset();
			updateCellState();
		} 
		
		//-------------------------------------------------------------------
		
		/** 
		 * Returns the result of the contents evaluation. The 
		 * contents input by the user are being evaluated.
		 */
		public function check():Boolean{
			return expressionManager.getResult();
		}
		//----------------------------- Mark Answer Skin --------------------------------------
		
		private var _markAnswerSkin:IMarkAnswerSkinWrapper;

	
		
		/**
		 * Sets the <code>MarkAnswerSkin</code> transmitted by the wrapper.
		 * The parent component should implement the wrapper.
		 */
		public function setMarkAnswerSkin(skin:IMarkAnswerSkinWrapper):void{
			_markAnswerSkin = skin;
			_markAnswerSkin.active = true; 
			
        	if (_markAnswerSkin != null  &&  _markAnswerSkin.view is UIComponent){
        		UIComponent(_markAnswerSkin.view).percentWidth = 100;
        		UIComponent(_markAnswerSkin.view).percentHeight = 100;
        	}
        	
			imageMarkAnswersBox.addChild(_markAnswerSkin.view);
			content.addChild(imageMarkAnswersBox);
		
        	
		}
		
		//-------------------------------------------------------------------
		
		/** Marks the cell visual state as correct if the contents are correct. */
		public function markIfCorrect(color:Number = 0xCCFFCC):void{
			if (!markingCorrect  &&  !markingWrong  &&  
				expressionManager.getResult()  &&  
				cellStateCode != CellState.FIXED  &&  !showingAnswer){ 
				unmark();
				markCorrect(color);
			}
		}
		
		/** Marks the cell visual state as correct. */ 
		public function markAsCorrect(color:Number = 0xCCFFCC):void{
			if (!markingCorrect  &&  !markingWrong  &&  
				cellStateCode != CellState.FIXED  &&  !showingAnswer){ 
				unmark();
				markCorrect(color);
			}
		}
		
		private function markCorrect(color:Number):void{
			if (_markAnswerSkin != null){
	        		
				_markAnswerSkin.correctness = true;
				_markAnswerSkin.markCorrect = true;
			} else {
				canvasBackgroundColorStd = getStyle("canvasBackgroundColor");
				setStyle("canvasBackgroundColor", color);
			}
			markingCorrect = true;
		}
		
		//-------------------------------------------------------------------
		
		/** Marks the cell visual state as wrong if the contents are wrong. */
		public function markIfWrong(color:Number = 0xFFCCCC):void{
			if (!markingCorrect  &&  !markingWrong  &&
				!expressionManager.getResult()  &&  (expressionManager.expressionContent.length > 0)  &&
				cellStateCode != CellState.FIXED  &&  !showingAnswer){
				unmark();
				markWrong(color);
				markingWrong = true;
			}
		}
		
		/** Marks the cell visual state as wrong. */
		public function markAsWrong(color:Number = 0xFFCCCC):void{
			if (!markingCorrect  &&  !markingWrong  &&  
				cellStateCode != CellState.FIXED  &&  !showingAnswer){
				unmark();
				markWrong(color);
				markingWrong = true;
			}
		}
		
		
		private function markWrong(color:Number):void{
			if (_markAnswerSkin != null){
				//content.addChild(imageMarkAnswersBox);
	        		
				_markAnswerSkin.correctness = false;
				_markAnswerSkin.markWrong = true;
			} else {
				canvasBackgroundColorStd = getStyle("canvasBackgroundColor");
				setStyle("canvasBackgroundColor", color);
			}
			markingWrong = true;
		}
		
		//-------------------------------------------------------------------
		
		/** Removes the visual indication of correct or wrong state. */
		public function unmark():void{
			if (markingCorrect  ||  markingWrong){
				
				if (_markAnswerSkin != null){
					//if (content.getChildren().indexOf(imageMarkAnswersBox) != -1)
					//	content.removeChild(imageMarkAnswersBox);
						
					if (markingCorrect)
						_markAnswerSkin.markCorrect = false;
					if (markingWrong)
						_markAnswerSkin.markWrong = false;
							 
				} else {
					setStyle("canvasBackgroundColor", canvasBackgroundColorStd);
				}
				
			}
		}
		
		//-------------------------------------------------------------------
		
		/** Unmarks the correct visual state indication */
		public function unmarkIfCorrect():void{
			if (markingCorrect){
				unmark();
				markingCorrect = false;
			}
		}
		
		//-------------------------------------------------------------------
		
		/** Unmarks the wrong visual state indication */
		public function unmarkIfWrong():void{
			if (markingWrong){
				unmark();
				markingWrong = false;
			}
		}
		
		//-------------------------------------------------------------------
		
		/** 
		 * If the contents input by the user can be
		 * evaluated as wrong, the cell is reset
		 */
		public function resetIfWrong():void{
			if (cellStateCode != CellState.FIXED){
				//if (_characterExpected.code != -1  &&  _characterExpected.code != cellData.character.code){
				/*
				if (_expressionExpected.length > 0 &&  !ArrayProcessor.compare(_expressionExpected, cellExpression)){
					reset();
				}
				*/
				if (!expressionManager.getResult()){
					reset();
				}
			}
		}
		
		//-------------------------------------------------------------------
		
		/** Shows the correct (expected or suggested) answer for the cell. */
		public function showAnswer():void{
			if (cellStateCode == CellState.FIXED){
				return;
			}
			
			if (_markAnswerSkin != null){
				//content.addChild(imageMarkAnswersBox);
				_markAnswerSkin.showAnswers = true;
			} 
			/*
			if (_expressionExpected.length > 0){
				
				unmark();
				cellStateCode = CellState.LOCKED;
				//cellData.character.fromCode(_characterExpected.code);
				userAnswer = cellExpression;
				cellExpression = ArrayProcessor.cloneRecursive(_expressionExpected);
				cellStateChanged();
				showingAnswer = true;
				
			} else if (_expressionSuggested.length > 0){
				
				unmark();
				cellStateCode = CellState.LOCKED;
				//cellData.character.fromCode(_characterSuggested.code);
				userAnswer = cellExpression;
				cellExpression = ArrayProcessor.cloneRecursive(_expressionSuggested);
				cellStateChanged();
				showingAnswer = true;
				
			} else{
				unmark();
				cellStateCode = CellState.LOCKED;
				//cellData.character.fromCode(_characterSuggested.code);
				userAnswer = cellExpression;
				cellExpression = new Array;
				cellStateChanged();
				showingAnswer = true;
			}
			*/
			
			unmark();
			showingAnswer = true;
			updateCellState();
			updateDisplayText();
			
		}
		
		//-------------------------------------------------------------------
		
		/** Hides the answer shown earlier. */ 
		public function hideAnswer():void{
			if (cellStateCode == CellState.FIXED  ||  showingAnswer == false){
				return;
			}
						
			if (_markAnswerSkin != null){
				//content.removeChild(imageMarkAnswersBox);
				_markAnswerSkin.showAnswers = false;
			} 
			/*
			//cellData.character.fromCode(-1);
			if (userAnswer  &&  userAnswer.length > 0){
				cellStateCode = CellState.LOCKED;
				cellExpression = userAnswer;
			} else{
				cellStateCode = CellState.OPEN;
				cellExpression = new Array();
			}
			userAnswer = null;
			cellStateChanged();
			showingAnswer = false;
			*/
			
			showingAnswer = false;
			updateCellState();
			updateDisplayText();
			
		}
		
		//-------------------------------------------------------------------
		
		/** Locks the cell's activity (drawing and inputing the characters. */
		public function lock():void{
			if (cellStateCode != CellState.FIXED){
				locked = true;
			}
		}
		
		//-------------------------------------------------------------------
		
		/** Unlocks the cell's activity (drawing and inputing the characters. */
		public function unlock():void{
			if (cellStateCode != CellState.FIXED){
				locked = false;
			}
		}
		
		
		//-------------------------------------------------------------------
		private function createResetImage():Image{
			var img:Image = new Image();
			img.source = new imgClearX as Bitmap;
			img.x = WIDTH - 16 - 1;
			img.y = 1;//HEIGHT - 16 - 1;
			img.visible = false;
			img.addEventListener(MouseEvent.CLICK, onClearClick);
			img.addEventListener(ResizeEvent.RESIZE, updateImageClearPosition);
			
			return img;
		}
		
		//-------------------------------------------------------------------
		
	}
}